package cn.buk.api.ctrip.service;

import cn.buk.api.ctrip.Factory.BaseFactory;
import cn.buk.api.ctrip.Factory.CreateRequestXmlFactory;
import cn.buk.api.ctrip.dto.tbooking.*;
import cn.buk.util.DateUtil;
import cn.buk.common.JsonResult;
import com.thoughtworks.xstream.XStream;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.XPath;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * Created by yfdai on 15/7/25.
 * API 文档地址: https://trade.ctrip.com/flight/ApiDoc/zh-cn/Booking/FlightApi/OpenApiExchange/RebookingTicketingResult
 */
public class TBookingServiceImpl extends BaseCtripApiService implements TBookingService  {

    private static final Logger logger = Logger.getLogger(TBookingServiceImpl.class);

    /**
     *  1. 今日出票单接口(未完成出票的）
     */
    private static final String URL_Search_OpenIssue_OrderList = "http://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/OpenIssueOrderList.asmx";

    /**
     *  2. 出票单查询接口
     */
    private static final String URL_Search_IssueBill = "http://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/SearchIssueBill.asmx";

    /**
     *  3. 出票单详情接口
     */
    private static final String URL_GET_Issue_Bill_Info = "http://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/IssueBillInfo.asmx";

    /**
     *  4. 出票单认领、取消认领接口
     *  接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/IssueBillAssignTo.asmx
     *  接口描述：出票单认领、取消认领接口
     */
    private static final String URL_Issue_Bill_Assign_To = "https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/IssueBillAssignTo.asmx";

    /**
     * 5. 票号回填
     *	票号回填接口请求地址
     */
    private static final String URL_Back_Fill_TicketNo = "http://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/SearchModifyOrderInfo.asmx";

    /**
     * 6. 已订位待支付订单查询接口
     */
    private static final String URL_UNPAID_ORDERS = "https://Flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/SearchUnpayOrderList.asmx";


    /**
     * 7. PNR修改接口
     */
    private static final String URL_ISSUE_BILL_MODIFY = "https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/IssueBillModify.asmx";

    /**
     * 8. PNR授权接口
     */
    private static final String URL_GET_PNR_PERMISSION = "https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/getpnrpermission.asmx";

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/CancelFlightRecordNo.asmx
//    接口描述：PNR取消接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/AbnormalTicketNoList.asmx
//    接口描述：票号待修改接口


//    接口地址：https://Flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/MergeIssueBills.asmx
//    接口描述：合并出票单接口


//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/RefundConfirm.asmx
//    接口描述：退票确认接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/RefundFeeWaitConfirmList.asmx
//    接口描述：退票费用待确认查询接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/CoopRefundRejectService.asmx
//    接口描述：退票/废票拒绝接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/GetCoopRefundConfirmListService.asmx
//    接口描述：退票处理查询列表接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/GetCoopRefundInvConfirmList.asmx
//    接口描述：废票处理查询列表接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/RefundInvConfirmService.asmx
//    接口描述：废票确认接口

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/SearchExchangeList.asmx
//    接口描述：改签处理查询列表接口请求地址

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/ModifyExchangeList.asmx
//    接口描述：改签操作接口请求地址

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/ReceiveRebookTicketInfoPrcocess.asmx
//    接口描述：改签票号回填

//    接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/RebookingTicketingResult.asmx
//    接口描述：改签票号回填(新)

    /**
     * 改签确认接口请求地址
     */
    private static final String Reconfirm_TicketNoList_URL = "http://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/ConfirmRebookAmountService.asmx";





    private boolean ignoreUnknownElements = true;

    private final String paraName = "requestXML";

    /**
     * 是否输出xml
     */
    private final boolean outputXml;

    public TBookingServiceImpl() {
        super();
        this.outputXml = false;
    }

    public TBookingServiceImpl(boolean outputXml) {
        super();
        this.outputXml = outputXml;
    }

    /**
     * 1. 今日出票单列表(只有未出票完成的）
     * https://trade.ctrip.com/flight/ApiDoc/zh-cn/Booking/FlightApi/OpenApiExchange/OpenIssueOrderList
     * 接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/OpenIssueOrderList.asmx
     * 接口描述：今日出票单接口
     */
    public List<TBookingOpenIssueBillInfo> searchOpenIssueOrderList(final String username, final String password, final String md5password, Date orderDate) {
        String beginTime = DateUtil.formatDate(orderDate, "yyyy-MM-dd") + "T00:00:00";
        String endTime   = DateUtil.formatDate(orderDate, "yyyy-MM-dd") + "T23:59:59";

        String params = CreateRequestXmlFactory.createTBookingOpenIssueOrderListRequestXml(username, password, md5password, beginTime, endTime);
        logger.debug(params);

        String responseXml = execApiRequest2(params, URL_Search_OpenIssue_OrderList, paraName);
        logger.debug(responseXml);

        return this.processOpenIssueBillResponse(responseXml);
    }

    /**
     * 2. 出票单查询接口
     * https://trade.ctrip.com/flight/ApiDoc/zh-cn/Booking/FlightApi/OpenApiExchange/SearchIssueBill
     * 接口地址：https://flights.ws.ctrip.com/Flight.Order.SupplierOpenAPI/SearchIssueBill.asmx

     接口描述：出票单查询接口
     */
    public List<TBookingIssueBillSearchResult> searchIssueBill(final String username, final String password, final String md5password, Date searchDate, Long issueBillId) {
        String strDate = DateUtil.formatDate(searchDate, "yyyy-MM-dd");

        String beginTime = strDate + " 00:00:00"; //日期间隔必须在一天内，支持时分秒
        String endTime   = strDate + " 23:59:59";

        String params = CreateRequestXmlFactory.createTBookingSearchIssueBillRequestXml(username, password, md5password, beginTime, endTime, issueBillId);
        if (this.outputXml)
            System.out.println(params);

        String responseXml = execApiRequest2(params, URL_Search_IssueBill, paraName);
        if (this.outputXml)
            System.out.println(responseXml);

        return this.processSearchIssueBillResponse(responseXml);
    }

    /**
     * 获取出票单详情
     * @param username
     * @param password
     * @param md5password
     * @param issueBillId
     * @return
     */
    public TBookingOpenIssueBillInfo getIssueBillInfo(String username, String password, String md5password, long issueBillId) {
        String params = CreateRequestXmlFactory.createTBookingIssueBillInfoRequestXml(username, password, md5password, issueBillId);

        String xml = execApiRequest2(params, URL_GET_Issue_Bill_Info, paraName);
        if (this.outputXml)
            System.out.println(xml);

        return processIssueBillInfoResponse(xml);
    }


    /**
     * 出票单认领、取消认领
     * @param username
     * @param password
     * @param md5password
     * @param issueBillId 出票单ID
     * @param opType 0：取消认领； 1：认领
     * @return
     */
    public JsonResult assignIssueBillTo(String username, String password, String md5password, long issueBillId, final int opType) {
        // 生成XML请求参数
        String params = CreateRequestXmlFactory.createTBookingIssueBillAssignToRequestXml(username, password, md5password, issueBillId, opType);

        final String xml = execApiRequest2(params, URL_Issue_Bill_Assign_To, paraName);

        if (this.outputXml)
            System.out.println(xml);

        Document document = convertXml2Document(xml);
        assert document != null: "document must be not null.";

        return processOpenIssueBillAssignResponse(document);
    }

    /**
     *
     * @param username
     * @param password
     * @param md5password
     * @return
     */
    public String searchReconfirmTicketList(final String username, final String password, final String md5password) {
        String params = CreateRequestXmlFactory.createTBookingReconfirmTicketNoListRequestXml(username, password, md5password);
//        System.out.println(params);

        String responseXml = execApiRequest2(params, Reconfirm_TicketNoList_URL, paraName);

        return responseXml;
    }

    public List<TBookingOpenIssueBillInfo> processSupplierOrderList(Document document) {
        if (document == null) return null;

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.error("ER#ResultCode is not Success.");
            logger.error(document.asXML());

            return null;
        }
        List<TBookingOpenIssueBillInfo> orderList = new ArrayList<TBookingOpenIssueBillInfo>();

        //此处需要在指定的命名空间里面才能找到
        //Map uris = new HashMap();
        //uris.put("ns", HOTEL_XML_NAMESPACE);
        XPath xpath = document.createXPath("//OrderInfoList/*");
        //xpath.setNamespaceURIs(uris);
        List myNodes = xpath.selectNodes(document);

        if (myNodes.size() == 0) {
            return orderList;
        }

        XStream xs = BaseFactory.createXStream(true);
        xs.processAnnotations(TBookingOpenIssueBillInfo.class);

        String xml;
        for (Iterator it = myNodes.iterator(); it.hasNext(); ) {
            Element element = (Element) it.next();

            xml = element.asXML();
            TBookingOpenIssueBillInfo orderInfo = (TBookingOpenIssueBillInfo) xs.fromXML(xml);
            orderList.add(orderInfo);
        }

        return orderList;
    }

    /**
     * 回填票号
     * @param username
     * @param password
     * @param md5password
     * @param request
     * @return
     */
    public JsonResult backFillTicketNo(final String username, final String password, final String md5password, TBookingBackFillTicketNoRequest request) {
        String params = CreateRequestXmlFactory.createTBookingBackFillTicketNoRequestXml(username, password, md5password, request);
        if (this.outputXml)
            System.out.println(params);

        String responseXml = execApiRequest2(params, URL_Back_Fill_TicketNo, paraName);
        if (this.outputXml)
            System.out.println(responseXml);

        return processOpenModifyOrderResponse(responseXml);
    }

    /**
     * 已订位待支付接口
     * @param username
     * @param password
     * @param md5password
     * @return
     */
    public List<OrderInfo> searchUnpaidOrders(String username, String password, String md5password) {
        String beginTime = DateUtil.formatDate(DateUtil.getCurDate(), "yyyy-MM-dd") + "T00:00:00";
        String params = CreateRequestXmlFactory.createTBookingOpenUnpayOrderListRequestXml(username, password, md5password, beginTime);
        String xml = execApiRequest2(params, URL_UNPAID_ORDERS, paraName);

        return processOpenOrderListResponse(convertXml2Document(xml));
    }

    /**
     * 修改出票单的编码
     * @param username
     * @param password
     * @param md5password
     * @return
     */
    public JsonResult updateIssueBillPnrNo(String username, String password, String md5password, IssueBillModifyRequest rq) {

        String requestXml = CreateRequestXmlFactory.createTBookingOpenIssueBillModifyRequestXml(username, password, md5password, rq);
//        System.out.println(requestXml);

        String xml = execApiRequest2(requestXml, URL_ISSUE_BILL_MODIFY, paraName);
//        System.out.println(xml);

        return processIssueBillModifyResponse(convertXml2Document(xml));
    }

    /**
     *
     * @param username
     * @param password
     * @param md5password
     * @param issueBillId 出票单ID
     * @param pnrNo 需要授权的编码
     * @param officeNo 需要授权的office号
     * @return
     */
    public JsonResult getPnrPermission(String username, String password, String md5password, long issueBillId, String pnrNo, String officeNo) {

        // 创建请求对象
        GetPnrPermissionRequest rq = new GetPnrPermissionRequest();
        rq.setIssueBillId(issueBillId);
        rq.setPnrNo(pnrNo);
        rq.setOfficeNo(officeNo);

        // 生成请求XML
        String requestXml = CreateRequestXmlFactory.createTBooking_GetPnrPermission_RequestXml(username, password, md5password, rq);
        if (this.outputXml)
            System.out.println(requestXml);

        String xml = execApiRequest2(requestXml, URL_GET_PNR_PERMISSION, paraName);
        if (this.outputXml)
            System.out.println(xml);

        return processCommonResponse(convertXml2Document(xml), "GetPnrPermissionResponse");
    }

    /**
     * 处理返回结果的通用部分
     * @param document xml文档
     * @param elementName 节点名称
     * @return
     */
    private JsonResult processCommonResponse(final Document document, final String elementName) {
        if (document == null) return JsonResult.createJsonResult(-1, "文档不能为null");

        JsonResult jsonResult = processResponseElementHeader(document.getRootElement().element("Header"));
        if (!JsonResult.STATUS_OK.equalsIgnoreCase(jsonResult.getStatus()))
            return jsonResult;

        Element element = document.getRootElement().element(elementName);
        int resultCode = Integer.parseInt(element.element("ResultCode").getText());
        String resultMessage = element.elementText("Message");

        if (resultCode == 0) {
            jsonResult.setStatus("OK");
            jsonResult.setDesc(resultMessage);
        }
        else {
            jsonResult.setStatus("ER");
        }

        jsonResult.setErrcode(resultCode);
        jsonResult.setErrmsg(resultMessage);

        return jsonResult;
    }

    public JsonResult processOpenModifyOrderResponse(String xml) {
        return processOpenModifyOrderResponse(convertXml2Document(xml));
    }

    public JsonResult processOpenModifyOrderResponse(Document document) {
        if (document == null) return null;

        JsonResult jsonResult = new JsonResult();

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.error("ER#ResultCode is not Success.");
            logger.error(document.asXML());
//            jsonResult.setStatus("ER");
//            jsonResult.setErrmsg("Header ResultCode is not success.");
//
//            return jsonResult;
        }

        Element element = rootElement.element("OpenModifyOrderResponse");
        int resultCode = Integer.parseInt(element.element("ResultCode").getText());
        String resultMessage = element.elementText("Message");

        if (resultCode == 0) jsonResult.setStatus("OK");
        else jsonResult.setStatus("ER");

        jsonResult.setErrcode(resultCode);
        jsonResult.setErrmsg(resultMessage);

        return jsonResult;
    }

    @Override
    public JsonResult processOpenIssueBillAssignResponse(Document document) {

        final String strXpath = "//OpenIssueBillAssignResponse/.";

        assert document != null : "document must be not null.";

        // 判断 Header 元素中的返回代码
        JsonResult jsonResult = processResponseElementHeader(document.getRootElement().element("Header"));
        if (!JsonResult.STATUS_OK.equalsIgnoreCase(jsonResult.getStatus()))
            return jsonResult;

        // 选择符合要求的节点
        Node node = document.createXPath(strXpath).selectSingleNode(document);

        XStream xs = BaseFactory.createXStream(true);
        xs.processAnnotations(OpenIssueBillAssignResponse.class);

//        System.out.println(node.asXML());
        OpenIssueBillAssignResponse response = (OpenIssueBillAssignResponse) xs.fromXML(node.asXML());

        if (response.isResult()) {
            return JsonResult.createJsonResult(1, response.getDescription());
        } else {
            return JsonResult.createJsonResult(-2, response.getDescription());
        }
    }

    @Override
    public List<OrderInfo> processOpenOrderListResponse(Document document) {
        assert document != null;

        JsonResult jsonResult = processResponseElementHeader(document.getRootElement().element("Header"));
        if (!JsonResult.STATUS_OK.equalsIgnoreCase(jsonResult.getStatus()))
            return new ArrayList<>();


        XPath xpath = document.createXPath("//OpenOrderListResponse/OrderInfoList/*");
        List myNodes = xpath.selectNodes(document);

        XStream xs = BaseFactory.createXStream(this.ignoreUnknownElements);
        xs.processAnnotations(OrderInfo.class);

        List<OrderInfo> results = new ArrayList<>();

        String xml;
        for (Iterator it = myNodes.iterator(); it.hasNext(); ) {
            Element element = (Element) it.next();

            xml = element.asXML();
            try {
                OrderInfo result = (OrderInfo) xs.fromXML(xml);
                results.add(result);
            } catch (Exception ex) {
                ex.printStackTrace();
                logger.error(xml);
            }
        }

        return results;
    }

    @Override
    public JsonResult processIssueBillModifyResponse(Document document) {
        if (document == null) return null;

        JsonResult jsonResult = processResponseElementHeader(document.getRootElement().element("Header"));
        if (!JsonResult.STATUS_OK.equalsIgnoreCase(jsonResult.getStatus()))
            return jsonResult;

        Element element = document.getRootElement().element("OpenIssueBillModifyResponse");

        boolean resultIsSuccess = Boolean.parseBoolean(element.element("Result").getText());
        String desc = element.elementText("Description");
//        String resultCode = element.elementText("ResultCode");

        if (resultIsSuccess) {
            jsonResult.setStatus(JsonResult.STATUS_OK);
            jsonResult.setDesc(desc);
        } else {
            jsonResult.setStatus(JsonResult.STATUS_ER);
            jsonResult.setErrmsg(desc);
        }

        return jsonResult;
    }

    public List<TBookingOpenIssueBillInfo> processOpenIssueBillResponse(String xml) {
        return processOpenIssueBillResponse(convertXml2Document(xml));
    }

    @Override
    public TBookingOpenIssueBillInfo processIssueBillInfoResponse(Document document) {
        TBookingOpenIssueBillInfo result = null;

        if (document == null) return result;

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.error("ER#ResultCode is not Success.");
            logger.error(document.asXML());
            return null;
        }

        XPath xpath = document.createXPath("//OpenIssueBillInfoResponse");
        List myNodes = xpath.selectNodes(document);

        if (myNodes.size() == 0) {
            return result;
        }

        XStream xs = BaseFactory.createXStream(this.ignoreUnknownElements);
        xs.processAnnotations(TBookingOpenIssueBillInfo.class);
        xs.alias("OpenIssueBillInfoResponse", TBookingOpenIssueBillInfo.class);
        xs.aliasField("IssueBillDetailInfoList", TBookingOpenIssueBillInfo.class, "details");

        xs.alias("IssueBillDetailInfo", TBookingOpenIssueBillDetail.class);
        xs.aliasField("CardType", TBookingOpenIssueBillDetail.class, "cardTypeDesc");
        xs.aliasField("AgencyRecordNO", TBookingOpenIssueBillDetail.class, "agencyPnrNo");
        xs.aliasField("CardTypeInt", TBookingOpenIssueBillDetail.class, "cardType");



        String xml;
        for (Iterator it = myNodes.iterator(); it.hasNext(); ) {
            Element element = (Element) it.next();

            xml = element.asXML();
            try {
                result = (TBookingOpenIssueBillInfo) xs.fromXML(xml);
            } catch (Exception ex) {
                ex.printStackTrace();
                logger.error(xml);
            }

            break;
        }

        return result;
    }


    @Override
    public TBookingOpenIssueBillInfo processIssueBillInfoResponse(String xml) {
        return processIssueBillInfoResponse(convertXml2Document(xml));
    }

    @Override
    public List<TBookingIssueBillSearchResult> processSearchIssueBillResponse(Document document) {
        if (document == null) return null;

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.error("ER#ResultCode is not Success.");
            logger.error(document.asXML());
            return null;
        }
        XPath xpath = document.createXPath("//OpenIssueBillSearchResultList/*");

        List myNodes = xpath.selectNodes(document);

        if (myNodes.size() == 0) {
            return null;
        }

        XStream xs = BaseFactory.createXStream(this.ignoreUnknownElements);
        xs.processAnnotations(TBookingIssueBillSearchResult.class);

        List<TBookingIssueBillSearchResult> list = new ArrayList<>();
        String xml;
        for (Iterator it = myNodes.iterator(); it.hasNext(); ) {
            Element element = (Element) it.next();

            xml = element.asXML();
            TBookingIssueBillSearchResult orderInfo = (TBookingIssueBillSearchResult) xs.fromXML(xml);
            list.add(orderInfo);

        }

        return list;
    }

    @Override
    public List<TBookingIssueBillSearchResult> processSearchIssueBillResponse(String xml) {
        return processSearchIssueBillResponse(convertXml2Document(xml));
    }

    public List<TBookingOpenIssueBillInfo> processOpenIssueBillResponse(Document document) {
        if (document == null) return null;

        Element rootElement = document.getRootElement();
        Element headerElement = rootElement.element("Header");
        if (!headerElement.attribute("ResultCode").getValue().equalsIgnoreCase("Success")) {
            logger.warn("ER#ResultCode is not Success.");
            logger.warn(document.asXML());
            return null;
        }
        //此处需要在指定的命名空间里面才能找到
        //Map uris = new HashMap();
        //uris.put("ns", HOTEL_XML_NAMESPACE);
        XPath xpath = document.createXPath("//IssueOrderList/*");
        //xpath.setNamespaceURIs(uris);
        List myNodes = xpath.selectNodes(document);

        if (myNodes.size() == 0) {
            return new ArrayList<>();
        }

        XStream xs = BaseFactory.createXStream(true);
        xs.processAnnotations(TBookingOpenIssueBillInfo.class);
        xs.aliasField("IssueBillID", TBookingOpenIssueBillDetail.class, "issueBillDetailId");


        List<TBookingOpenIssueBillInfo> list = new ArrayList<>();
        String xml;
        for (Iterator it = myNodes.iterator(); it.hasNext(); ) {
            Element element = (Element) it.next();

            xml = element.asXML();
            TBookingOpenIssueBillInfo orderInfo = (TBookingOpenIssueBillInfo) xs.fromXML(xml);
            list.add(orderInfo);

        }

        return list;
    }


    public void setIgnoreUnknownElements(boolean ignoreUnknownElements) {
        this.ignoreUnknownElements = ignoreUnknownElements;
    }
}
